﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;

namespace MoneyBook.Windows.Data {

    public abstract class AttachableCollection<T> : DependencyObjectCollection<T>, IAttachable where T : IAttachable {

        private DependencyObject associatedObject;
        private Collection<T> innerCollection;

        public AttachableCollection() {
            this.CollectionChanged += this.OnCollectionChanged;
            this.innerCollection = new Collection<T>();
        }

        public void Attach(DependencyObject dependencyObject) {
            if (dependencyObject != this.AssociatedObject) {
                if (this.AssociatedObject != null) {
                    throw new InvalidOperationException();
                }
                this.associatedObject = dependencyObject;
                foreach (T attachable in this) {
                    attachable.Attach(this.AssociatedObject);
                }
            }
        }

        public void Detach() {
            foreach (T attachable in this) {
                attachable.Detach();
            }
            this.associatedObject = null;
        }

        private void AddItem(T item) {

            bool contains = this.innerCollection.Contains(item);
            if (!contains) {
                if (this.AssociatedObject != null) {
                    item.Attach(this.AssociatedObject);
                }
            }
            this.innerCollection.Insert(base.IndexOf(item), item);
            if (contains) {
                throw new InvalidOperationException();
            }
        }

        private void RemoveItem(T item) {
            if (item.AssociatedObject != null) {
                item.Detach();
            }
            if (this.innerCollection.Contains(item)) {
                this.innerCollection.Remove(item);
            }
        }

        private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
            switch (e.Action) {
                case NotifyCollectionChangedAction.Add:
                    foreach (T newItem in e.NewItems) {
                        this.AddItem(newItem);
                    }
                    break;

                case NotifyCollectionChangedAction.Remove:
                    foreach (T oldItem in e.OldItems) {
                        this.RemoveItem(oldItem);
                    }
                    break;

                case NotifyCollectionChangedAction.Replace:
                    foreach (T oldItem in e.OldItems) {
                        this.RemoveItem(oldItem);
                    }
                    foreach (T newItem in e.NewItems) {
                        this.AddItem(newItem);
                    }
                    break;

                case NotifyCollectionChangedAction.Reset:
                    foreach (T localItem in this.innerCollection) {
                        this.RemoveItem(localItem);
                    }
                    this.innerCollection = new Collection<T>();
                    foreach (T localItem in this) {
                        this.AddItem(localItem);
                    }
                    break;

                default:
                    return;
            }
        }

        protected DependencyObject AssociatedObject {
            get {
                return this.associatedObject;
            }
        }

        DependencyObject IAttachable.AssociatedObject {
            get {
                return this.AssociatedObject;
            }
        }
    }
}